package io.renren.common.utils;

import io.renren.common.exception.RenException;
import io.renren.modules.gis_admin.constant.ApexConstants;
import io.renren.modules.gis_admin.dto.BasinConfigDTO;
import io.renren.modules.gis_admin.dto.GisDataDTO;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.DecimalFormat;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

/**
 * @author slc
 * @date 2023/9/23 21:14
 */
@Slf4j
public class ApexUtils {

  /**
   * 源数据校验
   * @param sourcePath 源数据路径
   */
  public static void isValidSource(String sourcePath) {
    File sourceDir = new File(sourcePath);
    File subFile = new File(sourcePath + File.separator + ApexConstants.SUB_FILE_NAME);
    if (!sourceDir.exists()) {
      throw new RenException("源数据目录不存在。");
    }
    if (!subFile.exists()) {
      throw new RenException("源数据中SUB1.SUB文件不存在。");
    }
  }

  /**
   * 源数据文件预处理
   * @param sourcePath 源数据路径
   * @throws IOException
   */
  public static void preInit(String sourcePath) throws IOException {
    // SUB1.SUB
    String subSource = sourcePath + File.separator + ApexConstants.SUB_FILE_NAME;
    String subTarget = sourcePath + File.separator + ApexConstants.SUB_TEMPLATE_FILE_NAME;
    fileReplace(subSource, subTarget);
    Files.deleteIfExists(Paths.get(subSource));
    // ASA
    String asaSource = sourcePath + File.separator + ApexConstants.RESULT_ASA_FILE_NAME;
    String asaTarget = sourcePath + File.separator + ApexConstants.RESULT_TEMPLATE_ASA_FILE_NAME;
    File asaFile = new File(asaSource);
    if (asaFile.exists()) {
      fileReplace(asaSource, asaTarget);
      Files.deleteIfExists(Paths.get(asaSource));
    }
  }

  /**
   * 配置执行入口
   * @param sourcePath 源数据路径
   * @param configList 配置
   * @return 图数据
   * @throws IOException
   */
  public static List<GisDataDTO> process(String sourcePath, List<BasinConfigDTO> configList) throws IOException, InterruptedException {
    configFileInit(sourcePath);
    generateConfigFile(sourcePath, configList);
    run(sourcePath);
    return gisData(sourcePath + File.separator + ApexConstants.RESULT_ASA_FILE_NAME);
  }

  /**
   * 配置文件初始化
   *
   * 1. SUB1.SUB
   * 2. TODO
   *
   * @param sourcePath 源数据路径
   * @throws IOException
   */
  private static void configFileInit(String sourcePath) throws IOException {
    fileReplace(
        sourcePath + File.separator + ApexConstants.SUB_TEMPLATE_FILE_NAME,
        sourcePath + File.separator + ApexConstants.SUB_FILE_NAME
    );
  }

  /**
   * 文件复制
   * @param templateFile 模板文件
   * @param targetFile 复制文件
   * @throws IOException
   */
  private static void fileReplace(String templateFile, String targetFile) throws IOException {
    Path templatePath = Paths.get(templateFile);
    Path targetPath = Paths.get(targetFile);
    Files.copy(templatePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
  }

  /**
   * 根据配置修改文件
   * 1. 土地类型 -> SUB1.SUB
   * 2. TODO 其它类型
   * @param sourcePath 源数据路径
   * @param configList 配置
   */
  public static void generateConfigFile(String sourcePath, List<BasinConfigDTO> configList) throws IOException {
    if (CollectionUtils.isEmpty(configList)) {
      return;
    }
    for (BasinConfigDTO config : configList) {
      if (config == null) {
        continue;
      }
      if (config.getLandType() != null) {
        landTypeConfigProcess(sourcePath, config.getBasinId(), config.getLandType());
      }
    }
  }

  /**
   * 执行软件
   * @param sourcePath 源数据路径
   */
  public static void run(String sourcePath) throws IOException, InterruptedException {
    log.info("run start");
    String executeFile = sourcePath + File.separator + ApexConstants.EXE_FILE_NAME;
    log.info("execute file: {}", executeFile);
    ProcessBuilder processBuilder = new ProcessBuilder(executeFile);
    processBuilder.directory(new File(sourcePath));
    Process process = processBuilder.start();
    int exitCode = process.waitFor();
    log.info("run end, code is {}", exitCode);
    process.destroy();
  }

  /**
   * 图数据
   * TODO：前面9行是没用的数据，注意观察后续是否适配
   * 注意：注意split之后第一个字符为空字符串
   * @param resultFile 结果ASA文件
   * @return 图数据
   */
  public static List<GisDataDTO> gisData(String resultFile) {
    try {
      List<GisDataDTO> result = Files.lines(Paths.get(resultFile))
          .skip(9)
          .map(line -> line.split("\\s+"))
          .map(arr -> {
            GisDataDTO dto = new GisDataDTO();
            dto.setId(nanConvert(arr[2]).intValue());
            dto.setYr(nanConvert(arr[3]).intValue());
            dto.setRus2(nanConvert(arr[25]));
            dto.setYn(nanConvert(arr[33]));
            dto.setQn(nanConvert(arr[34]));
            dto.setYp(nanConvert(arr[39]));
            dto.setQp(nanConvert(arr[40]));
            dto.setTn(nanConvert(dto.getYn() + dto.getQn() + ""));
            dto.setTp(nanConvert(dto.getYp() + dto.getQp() + ""));
            return dto;
          })
          .collect(Collectors.toList());
      return result;
    } catch (IOException e) {
      throw new RuntimeException("get gis data failed, sourcePath: " + resultFile, e);
    }
  }

  private static Float nanConvert(String str) {
    String convert = "NaN".equals(str) ? "0" : str;
    float floatValue = Float.parseFloat(convert);
    DecimalFormat df = new DecimalFormat("#.####");
    String formattedValue = df.format(floatValue);
    return Float.parseFloat(formattedValue);
  }

  /**
   * TODO 1. 观察其他参数修改过程 2. 同一文件统一修改，一次IO 3. 文件修改抽象成统一逻辑：位置、值
   * 土地类型配置: 修改SUB1.SUB中对应子流域数据的第二行，第十一个数字
   * @param sourcePath 源数据路径
   * @param basinId 子流域编号
   * @param landType 土地类型编号
   */
  public static void landTypeConfigProcess(String sourcePath, Long basinId, Long landType) throws IOException {
    String subFile = sourcePath + File.separator + ApexConstants.SUB_FILE_NAME;
    int row = (int) (basinId - 1) * 12 + 1;
    Path landTypePath = Paths.get(subFile);
    List<String> lines = Files.readAllLines(landTypePath, StandardCharsets.UTF_8);
    String secondLine = lines.get(row);
    // 匹配一个或多个空格
    String[] values = secondLine.trim().split("\\s+");
    values[10] = String.valueOf(landType);
    // 开头添加一个空格，和源数据格式保持一致，不确定不一致会不会影响，以防万一
    values[0] = "    " + values[0];
    secondLine = String.join("    ", values);
    lines.set(row, secondLine);
    Files.write(landTypePath, lines, StandardCharsets.UTF_8);
  }

  /**
   * 结果文件整理
   * @param sourcePath 源数据路径
   * @param resultParentPath 结果存放父目录
   * @return 结果文件路径
   */
  public static String moveResult(String sourcePath, String sourceName, String resultParentPath) {
    try {
      String recordDir = resultParentPath + File.separator + sourceName + "-" + System.currentTimeMillis();
      Files.createDirectories(Paths.get(recordDir));

      String resultFile = sourcePath + File.separator + ApexConstants.RESULT_ASA_FILE_NAME;
      String copyResultFile = recordDir + File.separator + ApexConstants.RESULT_ASA_FILE_NAME;
      Files.copy(Paths.get(resultFile), Paths.get(copyResultFile), StandardCopyOption.REPLACE_EXISTING);

      String subFile = sourcePath + File.separator + ApexConstants.SUB_FILE_NAME;
      String copySubFile = recordDir + File.separator + ApexConstants.SUB_FILE_NAME;
      Files.copy(Paths.get(subFile), Paths.get(copySubFile), StandardCopyOption.REPLACE_EXISTING);

      return copyResultFile;
    } catch (IOException e) {
      throw new RuntimeException(
          String.format("move file failed, sourcePath: %s, sourceName: %s, resultParentPath: %s", sourcePath, sourceName, resultParentPath),
          e);
    }
  }
}